<template>
  <div class="slider-container" @mousedown="onTrackMouseDown" ref="trackRef">
    <!-- 已选轨迹 -->
    <div class="slider-track" :style="{ width: thumbPercent + '%' }"></div>
    <!-- 滑块 -->
    <div
      class="slider-thumb"
      :style="{ left: thumbPercent + '%' }"
      @mousedown.stop="onThumbMouseDown"
    ></div>

    <!-- 刻度 -->
    <div class="slider-marks">
      <div
        v-for="(label, point) in marks"
        :key="point"
        class="slider-mark"
        :style="{ left: getPercent(point) + '%' }"
      >
        <div class="tick"></div>
        <div class="label">{{ getLabel(label) }}</div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, defineProps, defineEmits, onMounted } from 'vue'

const props = defineProps<{
  modelValue: number
  marks?: Record<number, string | { label?: string }>
  min?: number
  max?: number
  step?: number
}>()

const emit = defineEmits(['update:modelValue'])

const trackRef = ref<HTMLElement>()

const min = props.min ?? 0
const max = props.max ?? 100
const step = props.step ?? 1
const marks = props.marks ?? {}

const thumbPercent = computed(() => {
  return ((props.modelValue - min) / (max - min)) * 100
})

const getPercent = (val: number) => ((val - min) / (max - min)) * 100

const getLabel = (val: string | { label?: string }): string =>
  typeof val === 'string' ? val : val.label ?? ''

const updateValueFromX = (clientX: number) => {
  const track = trackRef.value
  if (!track) return

  const { left, width } = track.getBoundingClientRect()
  let percent = (clientX - left) / width
  percent = Math.max(0, Math.min(1, percent))

  const totalSteps = (max - min) / step
  const rawValue = min + percent * (max - min)
  const steppedValue = Math.round((rawValue - min) / step) * step + min

  emit('update:modelValue', Math.min(max, Math.max(min, steppedValue)))
}

const onTrackMouseDown = (e: MouseEvent) => {
  updateValueFromX(e.clientX)
  window.addEventListener('mousemove', onMouseMove)
  window.addEventListener('mouseup', onMouseUp)
}

const onThumbMouseDown = (e: MouseEvent) => {
  window.addEventListener('mousemove', onMouseMove)
  window.addEventListener('mouseup', onMouseUp)
}

const onMouseMove = (e: MouseEvent) => {
  updateValueFromX(e.clientX)
}

const onMouseUp = () => {
  window.removeEventListener('mousemove', onMouseMove)
  window.removeEventListener('mouseup', onMouseUp)
}
</script>

<style scoped>
.slider-container {
  position: relative;
  height: 9px;
  width: 100%;
  background: #eee;
  border-radius: 4px;
  cursor: pointer;
}

.slider-track {
  position: absolute;
  height: 100%;
  background: #409eff;
  border-radius: 4px;
  top: 0;
  left: 0;
  z-index: 1;
}

.slider-thumb {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 14px;
  height: 14px;
  background: white;
  border: 2px solid #409eff;
  border-radius: 50%;
  z-index: 2;
}

.slider-marks {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 100%;
  left: 0;
}

.slider-mark {
  position: absolute;
  transform: translateX(-50%);
  text-align: center;
  font-size: 12px;
  color: #666;
}

.slider-mark .tick {
  width: 1px;
  height: 6px;
  background: #999;
  margin: 0 auto;
}

.slider-mark .label {
  margin-top: 2px;
}
</style>
